package com.sun.StudentInfo.handler;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.sun.StudentInfo.pojo.Course;
import com.sun.StudentInfo.pojo.Grade;
import com.sun.StudentInfo.pojo.Profession;
import com.sun.StudentInfo.pojo.SC;
import com.sun.StudentInfo.pojo.StuExitSelect;
import com.sun.StudentInfo.pojo.StuSelectResult;
import com.sun.StudentInfo.pojo.Student;
import com.sun.StudentInfo.pojo.Vo.CourseVo;
import com.sun.StudentInfo.service.CoursePlanService;
import com.sun.StudentInfo.service.CourseService;
import com.sun.StudentInfo.service.GradeService;
import com.sun.StudentInfo.service.ProfessionService;
import com.sun.StudentInfo.service.SelectCourseService;
import com.sun.StudentInfo.service.StudentService;
import com.sun.StudentInfo.service.TeacherService;
import com.sun.StudentInfo.utils.ConflictUtil;
import com.sun.StudentInfo.utils.CourseSelectUtils;
import com.sun.StudentInfo.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;


@Controller
@RequestMapping("/StudentHandler")
public class StudentHandler {
    private static final String DEFAULT_KEY_PREFIX = "default_chosen_num_";

    private static final String STUDENT_COURSE_LIST = "student_course_list_";

    private static final String COURSE_SELECTED_NUM = "course_selected_num_";

    private static final String DEFAULT_VALUE = "default_value";

    @Autowired
    StudentService studentService;
    @Autowired
    TeacherService teacherService;
    @Autowired
    CoursePlanService coursePlanService;
    @Autowired
    CourseService courseService;
    @Autowired
    SelectCourseService selectCourseService;
    @Autowired
    GradeService gradeService;
    @Autowired
    ProfessionService professionService;
    @Resource
    RedisUtil redisUtil;
    @Resource
    CourseSelectUtils courseSelectUtils;

    // 查询个人信息
    @RequestMapping("/queryvitastu")
    public String queryVita(HttpServletRequest request, Model model) {
        Student student = getStudent(request);
        String sid = student.getSid();

        String credits = gradeService.queryCreditsSum(sid);

        Profession profession = professionService.getProfessionByProId(student.getProfession());

        model.addAttribute("sid", student.getSid());
        model.addAttribute("sname", student.getSname());
        model.addAttribute("sidcard", student.getSidcard());
        model.addAttribute("ssex", student.getSsex());
        model.addAttribute("sage", student.getSage());
        model.addAttribute("classr", student.getClassr());
        model.addAttribute("profession", profession.getProName());
        model.addAttribute("college", profession.getCollegeName());
        model.addAttribute("credits", credits);

        System.out.println(student);
        System.out.println(student.getSpassword());

        return "student/queryVitaStu";
    }

    // 跳转到修改密码页面
    @RequestMapping("/moditypwstu")
    public ModelAndView teacherModi(HttpServletRequest request, Model model) {
        return new ModelAndView(new RedirectView("/StudentInfo/student/modityPwStu.jsp"));
    }

    // 修改
    @RequestMapping("/moditypasswordstu")
    public ModelAndView teacherModiPw(@RequestParam("oldPassword") String oldPassword,
                                      @RequestParam("newPassword") String newPassword,
                                      HttpSession httpSession,
                                      HttpServletRequest request) {
        Student student = getStudent(request);
        String sid = student.getSid();

        // 修改密码要从数据库再查一遍密码
        student = studentService.getByStuSid(sid);
        if (!oldPassword.equals(student.getSpassword())) {
            request.setAttribute("msg", "密码输入错误！");
            return new ModelAndView(new RedirectView("/StudentInfo/fail.jsp"));
        }

        // 修改成功则退出到登录界面
        if (studentService.modifyStudentPwd(newPassword, sid) != 0) {
            httpSession.removeAttribute("sid");
            httpSession.removeAttribute("sname");
            httpSession.removeAttribute("courseList");
            httpSession.removeAttribute("ssrList");
            httpSession.removeAttribute("sesList");
            return new ModelAndView(new RedirectView("/StudentInfo/index.jsp"));
        }
        request.setAttribute("msg", "修改密码失败！");
        return new ModelAndView(new RedirectView("/StudentInfo/fail.jsp"));
    }

    // 查询学生所有课程
    @GetMapping(value = "/queryStudentCourse")
    public String redirect(HttpServletRequest request, Model model,
                           @RequestParam(value = "pageNo", required = false) Integer pageNo,
                           @RequestParam(value = "pageSize", required = false) Integer pageSize,
                           @RequestParam(value = "searchType", required = false) String searchType,
                           @RequestParam(value = "value", required = false) String value,
                           @RequestParam(value = "courseType", required = false) String courseType) {
        // 开启分页查询
        Map<String, Integer> pageNoAndPageSize = formatPageNoAndPageSize(pageNo, pageSize);
        PageHelper.startPage(pageNoAndPageSize.get("pageNo"), pageNoAndPageSize.get("pageSize"));

        request.setAttribute("searchType", searchType);
        request.setAttribute("value", value);
        request.setAttribute("courseType", courseType);

        // 1.获取session中的用户
        Student student = getStudent(request);

        // cookie session
        // HTTP请求是无状态的，
        // session

        // 2.根据用户中的学院id和专业id过滤课程
        String sid = student.getSid();
        String professionId = student.getProfession();
        if (StringUtils.isEmpty(professionId)) {
            System.err.println("学生专业不能为空");
            return "fail";
        }

        List<CourseVo> courseVoList = courseService.selectCourseByStu(sid, professionId, searchType, value, courseType);
        // 如果查询的课程集合为空，则返回错误页面
        if (courseVoList == null) {
            System.err.println("查询失败");
            return "fail";
        }

        // 把查询结果集放入PageInfo中
        pageIn(model, courseVoList);
        request.setAttribute("courseVoList", courseVoList);

        // 将获取的选修列表放入缓存，在选课的时候回显
        redisUtil.set(STUDENT_COURSE_LIST + sid, courseVoList);

        System.out.println("courseList: " + courseVoList);
        return "student/selCourse";
    }

    /**
     * 跳转到选课页面
     *
     * @param cid
     * @param model
     * @return
     */
    @RequestMapping(value = "/selcou/{cid}", method = RequestMethod.GET)
    public String selCou(@PathVariable(value = "cid") String cid,
                         HttpServletRequest request,
                         Model model) {

        Student student = getStudent(request);
        CourseVo courseVoFromCache = courseSelectUtils.getCourseVoFromCache(cid, student.getSid());
        if (courseVoFromCache == null) {
            request.setAttribute("msg", "获取数据失败，请稍后再试!");
            System.out.println("获取数据失败，请稍后再试!");
            return "fail";
        }

        model.addAttribute("courseVo", courseVoFromCache);
        return "student/confimCourse";
    }

    /**
     * 确认选课
     *
     * @param cid 课程id
     * @param totalNum 课程可容纳总人数
     * @return
     */
    @RequestMapping("/confirmCourse")
    public String confirmSelect(@RequestParam("cid") String cid,
                                @RequestParam("totalNum") int totalNum,
                                Model model,
                                HttpServletRequest httpRequest) {
        // 判断是否加入过此课程
        Student student = getStudent(httpRequest);
        String sid = student.getSid();
        if (selectCourseService.existCourse(cid, sid) != null) {
            httpRequest.setAttribute("msg", "已经加入过该课程，不能重复加入!");
            System.out.println("已经加入过该课程，不能重复加入!");
            return "fail";
        }

        // 从redis中已选课程数
        int selectedNum = courseSelectUtils.getSelectedNum(cid);

        // 如果选课已满，直接返回选课已满页面
        if (selectedNum >= totalNum) {
            httpRequest.setAttribute("msg", "课程已满，请选择其他课程!");
            return "fail";
        }

        // 设置的锁是针对课程而言的，同一时刻只能有一个人选课，这个人选了其他人才能选，
        // 这样就可以保证在并发场景下，最后选课人数不会超过总人数

        boolean locked = false;
        try {
            // 获取redis锁，返回true获取锁成功，可以执行选课操作，返回false说明有其他人在执行选课操作
            locked = redisUtil.setNx(DEFAULT_KEY_PREFIX + cid, DEFAULT_VALUE);

            // 如果没有获取到锁，则不能选课，先等待500ms，然后再去获取锁
            if (!locked) {
                Thread.sleep(500);
                for (int i = 0; i < 3; i++) {
                    // 再次获取锁
                    locked = redisUtil.setNx(DEFAULT_KEY_PREFIX + cid, DEFAULT_VALUE);
                    if (locked) {
                        // 如果拿到了锁，就跳出循环
                        break;
                    } else {
                        // 如果没有拿到锁，就继续等待500ms
                        Thread.sleep(500);
                    }
                }
                // 循环完之后再看看有没有拿到锁，如果没有拿到锁，就直接返回错误页面；拿到锁了就可以执行后面的选课操作了
                if (!locked) {
                    httpRequest.setAttribute("msg", "获取权限失败，请稍后再试!");
                    System.out.println("获取权限失败，请稍后再试!");
                    return "fail";
                }
            }

            // 从redis中已选课程数
            selectedNum = courseSelectUtils.getSelectedNum(cid);

            // 如果选课已满，直接返回选课已满页面
            if (selectedNum >= totalNum) {
                httpRequest.setAttribute("msg", "课程已满，请选择其他课程!");
                return "fail";
            }


            // 开始执行选课操作
            // 1.从缓存中获取courseVo信息，如果为空则返回错误页面，提升刷新后重试，因为这个缓存刷新了选课页面就会有
            CourseVo courseVoFromCache = courseSelectUtils.getCourseVoFromCache(cid, sid);
            if (courseVoFromCache == null) {
                httpRequest.setAttribute("msg", "获取数据失败，请稍后再试!");
                System.out.println("获取数据失败，请稍后再试!");
                return "fail";
            }

            // 2.判断选课时间是否冲突
            List<CourseVo> courseVoList = selectCourseService.getChosenCourses(student.getSid());
            if (!CollectionUtils.isEmpty(courseVoList)) {
                for (CourseVo courseVo : courseVoList) {
                    // 获取上课周
                    String chosenWeek = courseVo.getCourseWeek();
                    String toChooseWeek = courseVoFromCache.getCourseWeek();

                    // 转换上课周为set集合
                    Set<Integer> chosenWeekSet = ConflictUtil.parseWeek(chosenWeek);
                    Set<Integer> toChooseWeekSet = ConflictUtil.parseWeek(toChooseWeek);

                    // 如果上课周有交集，再判断上课时间
                    if (ConflictUtil.isConflict(chosenWeekSet, toChooseWeekSet)) {
                        // 获取上课时间
                        String chosenTime = courseVo.getCourseTime();
                        String toChooseTime = courseVoFromCache.getCourseTime();

                        // 转换上课周几和节次
                        Map<Integer, Set<Integer>> chosenMap = ConflictUtil.parseTime(chosenTime); // 已选课的周次和节次集合的map
                        Map<Integer, Set<Integer>> toChooseMap = ConflictUtil.parseTime(toChooseTime); // 待选课的周次和节次集合的map
                        boolean conflict = ConflictUtil.isConflict(chosenMap, toChooseMap);

                        String cName = courseVo.getCName();
                        // 如果返回true，则课程时间有冲突
                        if (conflict) {
                            httpRequest.setAttribute("msg", "与【" + cName + "】课程时间有冲突，请重新选课!");
                            System.out.println("课程时间有冲突，请重新选课!");
                            return "fail";
                        }
                    }
                }
            }

            SC sc = new SC();
            sc.setCid(cid);
            sc.setSid(sid);
            sc.setCourseType(courseVoFromCache.getType());
            sc.setCourseWeek(courseVoFromCache.getCourseWeek());
            sc.setCourseTime(courseVoFromCache.getCourseTime());

            // 3.更新sc
            int result = selectCourseService.saveSC(sc);
            if (result != 0) {
                // 4.更新已选人数
                int selectedNumByCid = courseSelectUtils.getSelectedNum(cid);
                redisUtil.set(COURSE_SELECTED_NUM + cid, ++selectedNumByCid);

                return "student/selectCourseSuccess";
            } else {
                return "fail";
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 执行完所有的操作后，如果前面拿到了锁，就要释放锁
            if (locked) {
                redisUtil.del(DEFAULT_KEY_PREFIX + cid);
            }
        }
        return "fail";
    }

    // 退出 ------
    @RequestMapping("/backseling/{cid}")
    public ModelAndView backConfirmSelect(@PathVariable(value = "cid") String cid) {

        return new ModelAndView(new RedirectView("/StudentInfo/StudentHandler/selqueryy/1"));

    }

    // 跳转页面  -------
    @GetMapping("/selqueryy/{pageNo}/{pageSize}")
    public String selQueryy(HttpServletRequest request,
                            @PathVariable(value = "pageNo") String pageNo,
                            @PathVariable(value = "pageSize") String pageSize,
                            Model model) {
        if (StringUtils.isEmpty(pageSize)) {
            pageSize = "3";
        }
        PageHelper.startPage(Integer.parseInt(pageNo), Integer.parseInt(pageSize));
        List<Course> courseList = null;
        pageIn(model, courseList);
        request.setAttribute("courseList", courseList);
        return "student/selCourse";
    }

    // 查看选课结果
    @RequestMapping(value = "/selcouresult", method = RequestMethod.GET)
    public String selcouresult(@RequestParam(value = "pageNo", required = false) Integer pageNo,
                               @RequestParam(value = "pageSize", required = false) Integer pageSize,
                               HttpServletRequest request,
                               Model model) {
        // 开启分页查询
        Map<String, Integer> pageNoAndPageSize = formatPageNoAndPageSize(pageNo, pageSize);
        PageHelper.startPage(pageNoAndPageSize.get("pageNo"), pageNoAndPageSize.get("pageSize"));

        Student student = getStudent(request);
        List<CourseVo> courseVoList = selectCourseService.getChosenCourses(student.getSid());
        System.out.println("已选课程：" + courseVoList);
        pageIn(model, courseVoList);
        request.setAttribute("courseVoList", courseVoList);

        return "student/selectedCourse";

    }

    // 所选课程列表详情  --------
    @RequestMapping(value = "/exitchoose/{sid}/{pn}", method = RequestMethod.GET)
    public String exitChoose(@PathVariable("sid") String sid, StuSelectResult ssr, HttpServletRequest request,
                             @PathVariable(value = "pn") String pn, Model model) {

        List<StuExitSelect> sesList = new ArrayList<StuExitSelect>();
        sesList = selectCourseService.getExitBysid(1, 10, sid);
        pageIn(model, sesList);
        request.setAttribute("sesList", sesList);

        return "student/exitSel";

    }

    // 退选
    @RequestMapping(value = "/exitsel/{cid}", method = RequestMethod.GET)
    public ModelAndView exitSel(HttpServletRequest request,
                                @PathVariable("cid") String cid) {
        Student student = getStudent(request);
        if (selectCourseService.deleteSC(cid, student.getSid()) != 0) {
            //缓存中数量减1
            int selectedNum = courseSelectUtils.getSelectedNum(cid);
            redisUtil.set(COURSE_SELECTED_NUM + cid, --selectedNum);

            return new ModelAndView(new RedirectView("/StudentInfo/StudentHandler/selcouresult"));
        } else {
            return new ModelAndView(new RedirectView("../fail.jsp"));
        }
    }

    // 学生查询本人选课
    @RequestMapping(value = "/endcourse", method = RequestMethod.GET)
    public String endCourse(HttpServletRequest request,
                            @RequestParam(value = "pageNo", required = false) Integer pageNo,
                            @RequestParam(value = "pageSize", required = false) Integer pageSize,
                            @RequestParam(value = "finishType", required = false) String finishType,
                            Model model) {
        request.setAttribute("finishType", finishType);

        // 开启分页查询
        Map<String, Integer> pageNoAndPageSize = formatPageNoAndPageSize(pageNo, pageSize);
        PageHelper.startPage(pageNoAndPageSize.get("pageNo"), pageNoAndPageSize.get("pageSize"));

        Student student = getStudent(request);
//        List<Grade> endCourseList = gradeService.getEedCourseBySid(1, 10, student.getSid());
        List<Grade> endCourseList = gradeService.getFinishCourse(student.getSid(),finishType);
        pageIn(model, endCourseList);
        request.setAttribute("endCourseList", endCourseList);

        // 获取获得的总学分
        int totalCredits = 0;
        if (!"noPass".equals(finishType)) {
            totalCredits = gradeService.getTotalCredits(student.getSid());
        }
        request.setAttribute("totalCredits", totalCredits);

        return "student/endCourse";

    }


    /**
     * 获取session中的用户
     */
    private Student getStudent(HttpServletRequest request) {
        HttpSession session = request.getSession();
        return (Student) session.getAttribute("student");
    }

    /**
     * 初始化pageNo和pageSize。如果默认值有变更，统一在这里修改
     */
    private Map<String, Integer> formatPageNoAndPageSize(Integer pageNo, Integer pageSize) {
        Map<String, Integer> map = new HashMap<>();
        if (pageNo == null || pageNo <= 0) {
            pageNo = 1;
        }
        if (pageSize == null || pageSize <= 0) {
            pageSize = 3;
        }
        map.put("pageNo", pageNo);
        map.put("pageSize", pageSize);
        return map;
    }

    /**
     * 把查询结果集放入PageInfo中
     * navigatePages 分页导航显示的页数
     */
    public void pageIn(Model model, List list) {
        PageInfo page = new PageInfo(list, 3);
        model.addAttribute("pageInfo", page);
        System.err.println(page);
    }


}
